/**
 * 
 */
package neptune.manager.builder;

import neptune.manager.TunerException;
import neptune.manager.model.topology.IPAddress;
import neptune.manager.model.topology.NetworkInterface;
import neptune.manager.model.topology.Node;
import neptune.manager.model.topology.Topology;
import neptune.manager.storage.ExperimentRepositoryException;
import neptune.manager.storage.StorageReadException;
import neptune.manager.storage.TopologyDefinitionException;
import neptune.manager.utils.DebugLog;
import neptune.manager.utils.IllegalValueException;
import neptune.manager.utils.Logger.LogContext;
import neptune.manager.utils.Logger.MessageType;

/**
 * @author Roberto Bifulco [RobertoBifulco.it] [info@robertoBifulco.it]
 * 
 */
public class ControlNetBuilder {

	private ControlIPAddressBuilder ipBuilder;

	public ControlNetBuilder(ControlIPAddressBuilder ipBuilder) {
		super();
		this.ipBuilder = ipBuilder;
	}

	public void prepareControlNetwork(Topology topology, LogContext log)
			throws TunerException {
		prepareControlNetwork(topology, ipBuilder, log);
	}

	public boolean verifyControlInterface(NetworkInterface controlIf,
			String topologyId, String node, String ifName, LogContext log)
			throws ExperimentRepositoryException, StorageReadException,
			TopologyDefinitionException {
		return verifyControlInterface(controlIf, topologyId, node, ifName,
				ipBuilder, log);
	}

	/**
	 * Modifica la topologia in modo da introdurre le interfacce di controllo,
	 * con gli opportuni indirizzi IP di controllo, laddove necessario. Allo
	 * stesso tempo verifica che le interfacce di controllo definite dall'utente
	 * siano corrette.<br>
	 * <br>
	 * 
	 * In caso di problemi nelle interfacce definite dall'utente, o di
	 * impossibilita' di configurazione della rete di controllo, solleva
	 * un'eccezione.
	 * 
	 * @param topology
	 * @param ipBuilder
	 * @param macBuilder
	 * @throws TunerException
	 */
	public static void prepareControlNetwork(Topology topology,
			ControlIPAddressBuilder ipBuilder, LogContext log)
			throws TunerException {

		boolean error = false;

		DebugLog.printDetailedLog("Starting topology tuner for topology '"
				+ topology.getName() + "'", ControlNetBuilder.class
				.getSimpleName());
		log.addMessage("Preparing control network", MessageType.NORMAL);

		for (Node node : topology.getNodes()) {
			LogContext nodeLog = log.createChild(node.getId());
			DebugLog.printDetailedLog("Analyzing node '" + node.getId() + "'",
					ControlNetBuilder.class.getSimpleName());
			nodeLog.addMessage("Analyzing node", MessageType.NORMAL);

			NetworkInterface controlIf = null;
			for (NetworkInterface netIf : node.getInterfaces()) {

				DebugLog.printDetailedLog("Analyzing network interface '"
						+ netIf.getInterfaceName() + "'",
						ControlNetBuilder.class.getSimpleName());
				nodeLog.addMessage("Analyzing network interface '"
						+ netIf.getInterfaceName() + "'", MessageType.NORMAL);

				if (netIf.getControlAddress() != null) {
					DebugLog.printDetailedLog("Control interface found '"
							+ netIf.getInterfaceName() + "'",
							ControlNetBuilder.class.getSimpleName());
					nodeLog.addMessage("Control interface found '"
							+ netIf.getInterfaceName() + "'",
							MessageType.NORMAL);
					controlIf = netIf;
				}
			}

			// Se il nodo ha definita una interfaccia con indirizzo IP di
			// controllo, verifica che l'indirizzo fornito sia effettivamente
			// nel range di indirizzi della rete di controllo e che non sia un
			// indirizzo gia' occupato
			if (controlIf != null) {
				boolean errorFlag = false;
				LogContext controlIfLog = nodeLog
						.createChild("controlIfBuilder");
				// Verifica che l'interfaccia di controllo sia la eth0
				if (!controlIf.getInterfaceName().equals("eth0")) {
					errorFlag = true;
					controlIfLog.addMessage(
							"Only eth0 can be used as control interface",
							MessageType.ERROR);
				} else {
					try {
						errorFlag = verifyControlInterface(controlIf, topology
								.getName(), node.getId(), controlIf
								.getInterfaceName(), ipBuilder, controlIfLog);
					} catch (Exception e) {
						errorFlag = true;
						controlIfLog.addMessage(
								"Impossible to verify control interface '"
										+ controlIf.getInterfaceName()
										+ "'. Cause: " + e.getMessage() + "\n",
								MessageType.ERROR);
					}
				}
				if (errorFlag) {
					error = true;
					nodeLog.addMessage(
							"Errors for node '" + node.getId() + "'",
							MessageType.ERROR);
				}
			}

			/*
			 * Se l'interfaccia non � definita, si deve provvedere a: 1.
			 * Definire una nuova interfaccia di controllo. 2. Assegnare a tale
			 * interfaccia un IP.
			 */
			if (controlIf == null) {
				DebugLog.printDetailedLog("Generating control interface",
						ControlNetBuilder.class.getSimpleName());
				nodeLog.addMessage("Generating control interface",
						MessageType.NORMAL);

				// Verifica se esiste l'interfaccia eth0
				NetworkInterface eth0 = null;
				for (NetworkInterface netIf : node.getInterfaces()) {
					if (netIf.getInterfaceName().equals("eth0")) {
						eth0 = netIf;
						nodeLog.addMessage("Eth0 found", MessageType.NORMAL);
					}
				}
				if (eth0 != null) {
					// Esiste eth0, ma non ha associato un indirizzo ip di
					// controllo
					try {
						eth0.addIpAddress(ipBuilder.buildControlIPAddress(
								topology, nodeLog.createChild("controlIp")));
					} catch (Exception e) {
						error = true;
						nodeLog
								.addMessage(
										"Errors for node '"
												+ node.getId()
												+ "':\n"
												+ "Unable to create control ip address. Cause: "
												+ e.getMessage() + "\n",
										MessageType.ERROR);
					}
				} else { // In caso contrario genera l'interfaccia
					try {
						DebugLog.printDetailedLog("Building interface",
								ControlNetBuilder.class.getSimpleName());
						nodeLog.addMessage("Building interface",
								MessageType.NORMAL);

						controlIf = InterfaceBuilder.buildNetInterface(node,
								nodeLog.createChild("interfaceBuilder"),
								topology);

						DebugLog.printDetailedLog("Building ip address",
								ControlNetBuilder.class.getSimpleName());

						controlIf.addIpAddress(ipBuilder.buildControlIPAddress(
								topology, nodeLog.createChild("controlIp")));
						node.addNetworkInterface(controlIf);

					} catch (Exception e) {
						error = true;
						nodeLog
								.addMessage(
										"Errors for node '"
												+ node.getId()
												+ "':\n"
												+ "Unable to create control network interface. Cause: "
												+ e.getMessage() + "\n",
										MessageType.ERROR);
					}
				}
			}

			if (error)
				throw new TunerException("Unable to prepare control network");
		}
	}

	/**
	 * Verifica se una interfaccia di controllo adopera un indirizzo IP di
	 * controllo corretto. In tal caso il metodo restituisce null, altrimenti
	 * restituisce una stringa con le indicazioni di errore.
	 * 
	 * @param controlIf
	 * @param usedControlAddresses
	 * @param controlAddresses
	 * @return isError
	 * @throws TopologyDefinitionException
	 * @throws StorageReadException
	 * @throws ExperimentRepositoryException
	 */
	public static boolean verifyControlInterface(NetworkInterface controlIf,
			String topologyId, String node, String ifName,
			ControlIPAddressBuilder ipBuilder, LogContext log)
			throws ExperimentRepositoryException, StorageReadException,
			TopologyDefinitionException {

		DebugLog.printDetailedLog("Starting control interface verification",
				ControlNetBuilder.class.getSimpleName());

		boolean error = false;
		IPAddress controlAddress = controlIf.getControlAddress();
		try {
			if (ipBuilder.getRange().isInRange(controlAddress)) {
				DebugLog.printDetailedLog(
						"Control address is in control net range:"
								+ controlAddress, ControlNetBuilder.class
								.getSimpleName());
				log.addMessage("Control address is in control net range:"
						+ controlAddress, MessageType.NORMAL);

				if (ipBuilder.addressExistsInRepository(controlAddress,
						topologyId, node, ifName)) {
					DebugLog.printDetailedLog("Control address already used:"
							+ controlAddress, ControlNetBuilder.class
							.getSimpleName());

					error = true;
					log
							.addMessage(
									"IP address "
											+ controlAddress
											+ " on interface '"
											+ controlIf.getInterfaceName()
											+ "' as control address, witch is already used by another node.\n",
									MessageType.ERROR);
				}
			} else {
				DebugLog.printDetailedLog(
						"Control address out of the control net range:"
								+ controlAddress, ControlNetBuilder.class
								.getSimpleName());

				error = true;
				log.addMessage("IP address " + controlAddress
						+ " on interface '" + controlIf.getInterfaceName()
						+ "' is defined as control address, but it is out "
						+ "of the range of control addresses.\n",
						MessageType.ERROR);
			}
		} catch (IllegalValueException e) {
			error = true;
			log.addMessage("Control address problem for interface '"
					+ controlIf.getInterfaceName() + "':" + e.getMessage()
					+ "/n", MessageType.ERROR);
		}

		return error;
	}

}
